前端:Vue3 对比 Vue2 有哪些变化? | 您所在的位置:网站首页 › vue 移动dom位置 › 前端:Vue3 对比 Vue2 有哪些变化? |
前言 希望本篇文章能帮你加深对 Vue 的理解,能信誓旦旦地说自己熟练Vue2/3。 内容混杂用法 + 原理 + 使用小心得,建议收藏,慢慢看。 区别生命周期的变化整体来看,变化不大,只是名字大部分需要 + on,功能上类似。使用上 Vue3 组合式 API 需要先引入;Vue2 选项 API 则可直接调用,如下所示。 //vue3 import{onMounted}from'vue' onMounted(()=>{ ... }) //可将不同的逻辑拆开成多个onMounted,依然按顺序执行,不被覆盖 onMounted(()=>{ ... }) //vue2 exportdefault{ mounted(){ ... }, } 常用生命周期表格如下所示。 Tips: setup是围绕beforeCreate和created生命周期钩子运行的,所以不需要显式地去定义。 多根节点Vue3 支持了多根节点组件,也就是fragment。 Vue2中,编写页面的时候,我们需要去将组件包裹在中,否则报错警告。 ... ... ... Vue3,我们可以组件包含多个根节点,可以少写一层,niceeee ! ... ... ... 异步组件Vue3 提供 Suspense组件,允许程序在等待异步组件时渲染兜底的内容,如 loading ,使用户体验更平滑。 使用它,需在模板中声明,并包括两个命名插槽:default和fallback。Suspense确保加载完异步内容时显示默认插槽,并将fallback插槽用作加载状态。 Loading... 真实的项目中踩过坑,若想在 setup 中调用异步请求,需在 setup 前加async关键字。这时,会受到警告async setup() is used without a suspense boundary。 解决方案:在父页面调用当前组件外包裹一层Suspense组件。 TeleportVue3 提供Teleport组件可将部分DOM移动到 Vue app之外的位置。比如项目中常见的Dialog组件。 点击 组合式APIVue2 是 选项式API(Option API),一个逻辑会散乱在文件不同位置(data、props、computed、watch、生命周期函数等),导致代码的可读性变差,需要上下来回跳转文件位置。Vue3 组合式API(Composition API)则很好地解决了这个问题,可将同一逻辑的内容写到一起。 除了增强了代码的可读性、内聚性,组合式API 还提供了较为完美的逻辑复用性方案,举个 ,如下所示公用鼠标坐标案例。 //main.vue mouseposition{{x}}{{y}} import{ref}from'vue' importuseMousePositionfrom'./useMousePosition' const{x,y}=useMousePosition() } //useMousePosition.js import{ref,onMounted,onUnmounted}from'vue' functionuseMousePosition(){ letx=ref(0) lety=ref(0) functionupdate(e){ x.value=e.pageX y.value=e.pageY } onMounted(()=>{ window.addEventListener('mousemove',update) }) onUnmounted(()=>{ window.removeEventListener('mousemove',update) }) return{ x, y } } 解决了 Vue2 Mixin的存在的命名冲突隐患,依赖关系不明确,不同组件间配置化使用不够灵活。 响应式原理Vue2 响应式原理基础是Object.defineProperty;Vue3 响应式原理基础是 Proxy。 Object.defineProperty基本用法:直接在一个对象上定义新的属性或修改现有的属性,并返回对象。 Tips: writable 和 value 与 getter 和 setter 不共存。 letobj={} letname='瑾行' Object.defineProperty(obj,'name',{ enumerable:true,//可枚举(是否可通过for...in或Object.keys()进行访问) configurable:true,//可配置(是否可使用delete删除,是否可再次设置属性) //value:'',//任意类型的值,默认undefined //writable:true,//可重写 get:function(){ returnname }, set:function(value){ name=value } }) 搬运 Vue2 核心源码,略删减。 functiondefineReactive(obj,key,val){ //一key一个dep constdep=newDep() //获取key的属性描述符,发现它是不可配置对象的话直接return constproperty=Object.getOwnPropertyDescriptor(obj,key) if(property&&property.configurable===false){return} //获取getter和setter,并获取val值 constgetter=property&&property.get constsetter=property&&property.set if((!getter||setter)&&arguments.length===2){val=obj[key]} //递归处理,保证对象中所有key被观察 letchildOb=observe(val) Object.defineProperty(obj,key,{ enumerable:true, configurable:true, //get劫持obj[key]的进行依赖收集 get:functionreactiveGetter(){ constvalue=getter?getter.call(obj):val if(Dep.target){ //依赖收集 dep.depend() if(childOb){ //针对嵌套对象,依赖收集 childOb.dep.depend() //触发数组响应式 if(Array.isArray(value)){ dependArray(value) } } } } returnvalue }) //set派发更新obj[key] set:functionreactiveSetter(newVal){ ... if(setter){ setter.call(obj,newVal) }else{ val=newVal } //新值设置响应式 childOb=observe(val) //依赖通知更新 dep.notify() } } 那 Vue3 为何会抛弃它呢?那肯定是有一些缺陷的。 主要原因:无法监听对象或数组新增、删除的元素。Vue2 方案:针对常用数组原型方法push、pop、shift、unshift、splice、sort、reverse进行了hack处理;提供Vue.set监听对象/数组新增属性。对象的新增/删除响应,还可以new个新对象,新增则合并新属性和旧对象;删除则将删除属性后的对象深拷贝给新对象。 Tips: Object.defineOProperty是可以监听数组已有元素,但 Vue2 没有提供的原因是性能问题,具体可看见参考第二篇 ~。 ProxyProxy是ES6新特性,通过第2个参数handler拦截目标对象的行为。相较于Object.defineProperty提供语言全范围的响应能力,消除了局限性。但在兼容性上放弃了(IE11以下) 局限性 对象/数组的新增、删除。监测.length修改。Map、Set、WeakMap、WeakSet的支持。基本用法:创建对象的代理,从而实现基本操作的拦截和自定义操作。 consthandler={ get:function(obj,prop){ returnpropinobj?obj[prop]:'' }, set:function(){}, ... } 搬运 Vue3 的源码 reactive.ts 文件 functioncreateReactiveObject(target,isReadOnly,baseHandlers,collectionHandlers,proxyMap){ ... //collectionHandlers:处理Map、Set、WeakMap、WeakSet //baseHandlers:处理数组、对象 constproxy=newProxy( target, targetType===TargetType.COLLECTION?collectionHandlers:baseHandlers ) proxyMap.set(target,proxy) returnproxy } 以 baseHandlers.ts 为例,使用Reflect.get而不是target[key]的原因是receiver参数可以把this指向getter调用时,而非Proxy构造时的对象。 //依赖收集 functioncreateGetter(isReadonly=false,shallow=false){ returnfunctionget(target:Target,key:string|symbol,receiver:object){ ... //数组类型 consttargetIsArray=isArray(target) if(!isReadonly&&targetIsArray&&hasOwn(arrayInstrumentations,key)){ returnReflect.get(arrayInstrumentations,key,receiver) } //非数组类型 constres=Reflect.get(target,key,receiver); //对象递归调用 if(isObject(res)){ returnisReadonly?readonly(res):reactive(res) } returnres } } //派发更新 functioncreateSetter(){ returnfunctionset(target:Target,key:string|symbol,value:unknown,receiver:Object){ value=toRaw(value) oldValue=target[key] //因ref数据在setvalue时就已trigger依赖了,所以直接赋值return即可 if(!isArray(target)&&isRef(oldValue)&&!isRef(value)){ oldValue.value=value returntrue } //对象是否有key有keyset,无keyadd consthadKey=hasOwn(target,key) constresult=Reflect.set(target,key,value,receiver) if(target===toRaw(receiver)){ if(!hadKey){ trigger(target,TriggerOpTypes.ADD,key,value) }elseif(hasChanged(value,oldValue)){ trigger(target,TriggerOpTypes.SET,key,value,oldValue) } } returnresult } } 虚拟DOMVue3 相比于 Vue2 虚拟DOM 上增加patchFlag字段。我们借助Vue3 Template Explorer来看。 技术摸鱼 今天天气真不错 {{name}} 渲染函数如下。 import{createElementVNodeas_createElementVNode,toDisplayStringas_toDisplayString,openBlockas_openBlock,createElementBlockas_createElementBlock,pushScopeIdas_pushScopeId,popScopeIdas_popScopeId}from"vue" const_withScopeId=n=>(_pushScopeId("scope-id"),n=n(),_popScopeId(),n) const_hoisted_1={id:"app"} const_hoisted_2=/*#__PURE__*/_withScopeId(()=>/*#__PURE__*/_createElementVNode("h1",null,"技术摸鱼",-1/*HOISTED*/)) const_hoisted_3=/*#__PURE__*/_withScopeId(()=>/*#__PURE__*/_createElementVNode("p",null,"今天天气真不错",-1/*HOISTED*/)) exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createElementBlock("div",_hoisted_1,[ _hoisted_2, _hoisted_3, _createElementVNode("div",null,_toDisplayString(_ctx.name),1/*TEXT*/) ])) } 注意第 3 个_createElementVNode的第 4 个参数即patchFlag字段类型,字段类型情况如下所示。1 代表节点为动态文本节点,那在 diff 过程中,只需比对文本对容,无需关注 class、style等。除此之外,发现所有的静态节点,都保存为一个变量进行静态提升,可在重新渲染时直接引用,无需重新创建。 exportconstenumPatchFlags{ TEXT=1,//动态文本内容 CLASS=1 |
CopyRight 2018-2019 实验室设备网 版权所有 |